package Test;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import Dispatcher.GroupDispatcher;
import Dispatcher.UpdateThread;
import View.BuildingGUI;


public class FourElevatorsStressTest {
	private static GroupDispatcher dispatcher;
	private static BuildingGUI gui;
	
	//create the group dispatcher
	@BeforeClass
	public static void setUp() throws Exception {
		dispatcher = new GroupDispatcher();
		gui = new BuildingGUI(dispatcher);
		gui.setVisible(true);
		UpdateThread ut = new UpdateThread(dispatcher);
		ut.start();

	}
	
	//after all tests have run, close sockets and other clean-up
	@AfterClass
	public static void tearDown() throws Exception {
		dispatcher.finalize();
	}
	
	//people getting back home - elevator almost filled to maximum
	//scenario
	//elevators stops at floors 2, 4, 6, 7, 10
	//then back to floor 1 to collect more people
	//then stops at floors 2 and 4
	//back to floor 1
	//again back up to 3 and 7
	//back to floor 1
	//then back up to 5
	//finally back to floor 1
	@Test
	public void testAfternoonRushWorstCaseResponseTime() throws InterruptedException {
		Thread.sleep(5000);
		long[] durations = new long[5];
		
		for (int i = 0; i < 5; i++) {
			Thread.sleep(5000);
			long start = System.currentTimeMillis();
			processHallCall(1, 2, 1);
			Thread.sleep(100);
			processHallCall(1, 4, 1);
			Thread.sleep(100);
			processHallCall(1, 6, 1);
			Thread.sleep(100);
			processHallCall(1, 7, 1);
			Thread.sleep(100);
			processHallCall(1, 10, 1);
			Thread.sleep(100);
			processHallCall(3, 3, 1);
			Thread.sleep(100);
			processHallCall(3, 7, 1);
			Thread.sleep(100);
			processHallCall(4, 5, 1);
			Thread.sleep(100);
			processHallCall(2, 2, 1);
			Thread.sleep(100);
			processHallCall(2, 4, 1);
			Thread.sleep(100);
			
			while(dispatcher.status.get(4).getCurrentFloor() != 5) Thread.sleep(1);
			processHallCall(4, 1, 1);
			while(dispatcher.status.get(3).getCurrentFloor() != 7) Thread.sleep(1);
			processHallCall(3, 1, 1);
			while(dispatcher.status.get(2).getCurrentFloor() != 4) Thread.sleep(1);
			processHallCall(2, 1, 1);
			while(dispatcher.status.get(1).getCurrentFloor() != 10) Thread.sleep(1);
			processHallCall(1, 1, 1);
			
			while(dispatcher.status.get(4).getCurrentFloor() != 1) Thread.sleep(1);
			while(dispatcher.status.get(3).getCurrentFloor() != 1) Thread.sleep(1);
			while(dispatcher.status.get(2).getCurrentFloor() != 1) Thread.sleep(1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1) Thread.sleep(1);
			
			durations[i] = System.currentTimeMillis() - start;
		}
		
		this.writeToFile("Now we run the same scenarios with 4 elevators\n" +
				"We should see a decrease in the time required " +
				"to run the scenario as there are 4 elevators instead of 1\n");
		
		double average = calculateAverage(durations);
		double stdDev = calculateStdDev(durations, average);
		double[] durationsInSec = durationsInSeconds(durations);
		
		for (int i = 0; i < durations.length; i++) {
			this.writeToFile(durationsInSec[i] + "\n");
		}
		
		this.writeToFile("Afternoon Rush Scenario Average Time: " + average + "s " +
					"with standard deviation: " + stdDev + "s\n\n");
	}
	
	//people leaving in the morning - elevator almost filled to maximum
	//scenario
	//elevator at floor 1
	//request at floor 5 to go to floor 1
	//elevator at floor 1
	//go back up to floors 10, 8, 6, 3 to go to floor 1
	//elevator at floor 1
	//back up to floors 3 and 7
	//elevator at floor 1
	//back up to floors 6 and 4
	//elevator at floor 1
	@Test
	public void testMorningRushWorstCaseResponseTime() throws InterruptedException {
		Thread.sleep(5000);
		long[] durations = new long[5];
		
		for(int i = 0; i<5; i++){
			Thread.sleep(5000);
			long start = System.currentTimeMillis();
			
			//request at floor 5
			processHallCall(1, 5, 1);
			Thread.sleep(100);
			processHallCall(2, 10, 1);
			Thread.sleep(100);
			processHallCall(2, 8, 1);
			Thread.sleep(100);
			processHallCall(2, 6, 1);
			Thread.sleep(100);
			processHallCall(2, 3, 1);
			Thread.sleep(100);
			processHallCall(3, 7, 1);
			Thread.sleep(100);
			processHallCall(3, 3, 1);
			Thread.sleep(100);
			processHallCall(4, 6, 1);
			Thread.sleep(100);
			processHallCall(4, 4, 1);
			Thread.sleep(100);
			
			while(dispatcher.status.get(1).getCurrentFloor() != 5) Thread.sleep(1);
			processHallCall(1, 1, 1);
			while(dispatcher.status.get(3).getCurrentFloor() != 3) Thread.sleep(1);
			processHallCall(3, 1, 1);
			while(dispatcher.status.get(4).getCurrentFloor() != 4) Thread.sleep(1);
			processHallCall(4, 1, 1);
			while(dispatcher.status.get(2).getCurrentFloor() != 3) Thread.sleep(1);
			processHallCall(2, 1, 1);
			
			while(dispatcher.status.get(4).getCurrentFloor() != 1) Thread.sleep(1);
			while(dispatcher.status.get(3).getCurrentFloor() != 1) Thread.sleep(1);
			while(dispatcher.status.get(2).getCurrentFloor() != 1) Thread.sleep(1);
			while(dispatcher.status.get(1).getCurrentFloor() != 1) Thread.sleep(1);
			
			durations[i] = System.currentTimeMillis() - start;
		}
		
		double average = calculateAverage(durations);
		double stdDev = calculateStdDev(durations, average);
		double[] durationsInSec = durationsInSeconds(durations);
		
		for (int i = 0; i < durationsInSec.length; i++) {
			this.writeToFile(durationsInSec[i] + "\n");
		}
		
		this.writeToFile("Morning Rush Scenario Average Time: " + average + "s " +
					"with standard deviation: " + stdDev + "s\n\n");
	}
	
	//--------------------Helpers---------------------------
	private void writeToFile(String s){
		File file = new File("./documents/Milestone-4/StressTests.txt");
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
			writer.write(s);
			writer.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public double calculateAverage(long[] durations) {
		long sum = 0;
		for (int i = 0; i < durations.length; i++) {
			sum += durations[i];
		}
		return (sum / durations.length)/1000.0;
	}
	
	public double calculateStdDev(long[] durations, double average) {
		double avgInMilli = average*1000;
		long squaredDiff = 0;
		for (int i = 0; i < durations.length; i++) {
			squaredDiff += Math.pow(durations[i] - avgInMilli, 2);
		}
		return Math.sqrt(squaredDiff / durations.length)/1000.0;
	}
	
	public double[] durationsInSeconds(long[] durations) {
		double[] result = new double[durations.length];
		for (int i = 0; i < durations.length; i++) {
			result[i] = durations[i]/1000.0;
		}
		return result;
	}
	
	public void processHallCall(int index, int floor, int dir) {
		dispatcher.elevators.get(index).println(
				5 + "/" + floor + "/" + dir + "/" + "\n");
	}
}
